An open-source workflow
October 17, 2024
Henry BeimersSection 1: Why R and D3?
The D3 JavaScript library
The r2d3 R Package
Section 2: When should I use r2d3 for my maps?
Section 2: When should I use r2d3 for my maps?
For creating static maps in R
For creating interactive graphics
Section 2: When should I use r2d3 for my maps?
D3 graphics can be displayed in HTML documents
D3 graphics are by default SVG graphics
Section 3: Basics of r2d3
Suppose you need to display a map of median home values for Pierce County, WA, but need to quickly see different levels of geographic aggregation within the county:
Section 3: Basics of r2d3
Get data at the census tract and block group level using tidycensus1
map_data_cbg <- tidycensus::get_acs(geography = "cbg",
variables = "B25077_001",
state = "WA",
county = "Pierce",
geometry = TRUE) %>%
dplyr::select(GEOID, estimate, geometry) %>%
dplyr::mutate(level = "cbg")
map_data_tract <- tidycensus::get_acs(geography = "tract",
variables = "B25077_001",
state = "WA",
county = "Pierce",
geometry = TRUE) %>%
dplyr::select(GEOID, estimate, geometry) %>%
dplyr::mutate(level = "tract")
all_data <- bind_rows(map_data_cbg, map_data_tract)r2d3() functionSection 3: Basics of r2d3
shiny::selectInput("geogs", "Geography:", choices = c("cbg","tract"))
shiny::selectInput("na_color", "NA color:", choices = c("gray","darkgray","lightgray"))
r2d3::d3Output("choroMap")
output$choroMap <- r2d3::renderD3({
r2d3::r2d3(data = all_data,
script = "d3_map_example.js",
options = list(na_color = input$na_color,
geog = input$geogs,
zoom = TRUE))
})r2d3() functionSection 3: Basics of r2d3
Section 3: Basics of r2d3
// Remove previous svg
svg.selectAll("g").remove();
// Transform data into json-style format
var filteredData = {
"type": "FeatureCollection",
"features": data
.filter(d => d.level === options.geog) // Filter to the selected geography
.map(d => ({
"type": "Feature",
"geometry": d.geometry,
"properties": {
"GEOID": d.GEOID,
"estimate": d.estimate,
"level": d.level,
}
}))
};
// Define the D3 map projection
var my_projection = d3.geoMercator().fitSize([width, height], filteredData);
var path = d3.geoPath().projection(my_projection);
// Create a blue color ramp for our target variable
const colorScale = d3.scaleSequential(d3.interpolateBlues)
.domain([0, d3.max(filteredData.features, d => d.properties.estimate)]);
// Define zoom behavior
var zoom = d3.zoom()
.scaleExtent([1, 8])
.on("zoom", zoomed);
// Apply zoom behavior to the SVG
svg.call(zoom);
// Create a group to hold the map paths (this will help with zooming and panning)
var g = svg.append("g");
// Create the map
g.selectAll('path')
.data(filteredData.features)
.enter()
.append('path')
.attr('d', path)
.attr('fill', d => {
// check if estimate is valid, otherwise set to NA color
return d.properties.estimate != null && !isNaN(d.properties.estimate)
? colorScale(d.properties.estimate)
: options.na_color;
})
.on("mouseover", function(event, d) {
// Show the tooltip on mouseover
tooltip.transition().duration(200).style("opacity", 1);
tooltip.html("GEOID: " + d.properties.GEOID + "<br>Estimate: " + d.properties.estimate)
.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 28) + "px");
d3.select(this)
.style("stroke", "black")
.style("stroke-width", 1.5);
})
.on("mousemove", function(event) {
// Move the tooltip with the mouse
tooltip.style("left", (event.pageX + 10) + "px")
.style("top", (event.pageY - 28) + "px");
})
.on("mouseout", function() {
// Hide the tooltip on mouseout
tooltip.transition().duration(500).style("opacity", 0);
d3.select(this).style("stroke", "none");
});
// Add a tooltip div to the body
var tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("padding", "8px")
.style("background-color", "rgba(0, 0, 0, 0.6)")
.style("color", "#fff")
.style("border-radius", "4px")
.style("pointer-events", "none")
.style("opacity", 0)
// Make sure tooltip is in front of all other elements
.style("z-index", "1000");
// Zoom function
function zoomed(event) {
if (options.zoom === true) {
g.attr("transform", event.transform);
}
}Section 4: Examples
Section 4: Examples
Section 4: Examples
https://drive.google.com/drive/folders/1NrlAKRdM8tSmcA4cTk7mqJKJ5q_vnLbd?usp=sharing